home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Shells / zsh / Source / src / exec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-07  |  42.4 KB  |  2,117 lines

  1. /*
  2.  *
  3.  * exec.c - command execution
  4.  *
  5.  * This file is part of zsh, the Z shell.
  6.  *
  7.  * This software is Copyright 1992 by Paul Falstad
  8.  *
  9.  * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  10.  * use this software as long as: there is no monetary profit gained
  11.  * specifically from the use or reproduction of this software, it is not
  12.  * sold, rented, traded or otherwise marketed, and this copyright notice is
  13.  * included prominently in any copy made.
  14.  *
  15.  * The author make no claims as to the fitness or correctness of this software
  16.  * for any use whatsoever, and it is provided as is. Any use of this software
  17.  * is at the user's own risk.
  18.  *
  19.  */
  20.  
  21. #include "zsh.h"
  22. #include <errno.h>
  23.  
  24. #define execerr() { if (forked) _exit(1); \
  25.     closemnodes(mfds); lastval = 1; return; }
  26.  
  27. static Lklist args;
  28.  
  29. /* parse list in a string */
  30.  
  31. List parselstring(s)        /**/
  32. char *s;
  33. {
  34.     List l;
  35.  
  36.     hungets(s);
  37.     strinbeg();
  38.     pushheap();
  39.     if (!(l = parse_list())) {
  40.     strinend();
  41.     hflush();
  42.     popheap();
  43.     return NULL;
  44.     }
  45.     strinend();
  46.     return l;
  47. }
  48.  
  49. /* execute a string */
  50.  
  51. void execstring(s)        /**/
  52. char *s;
  53. {
  54.     List l;
  55.  
  56.     if ((l = parselstring(s))) {
  57.     execlist(l);
  58.     popheap();
  59.     }
  60. }
  61.  
  62. /* fork and set limits */
  63.  
  64. int phork()
  65. {                /**/
  66.     int pid;
  67. #ifdef RLIM_INFINITY
  68.     int t0;
  69. #endif
  70.  
  71.     if (thisjob >= MAXJOB - 1) {
  72.     zerr("job table full", NULL, 0);
  73.     return -1;
  74.     }
  75.     pid = fork();
  76.     if (pid == -1) {
  77.     zerr("fork failed: %e", NULL, errno);
  78.     return -1;
  79.     }
  80. #ifdef RLIM_INFINITY
  81.     if (!pid)
  82.     for (t0 = 0; t0 != RLIM_NLIMITS; t0++)
  83.         setrlimit(t0, limits + t0);
  84. #endif
  85.     return pid;
  86. }
  87.  
  88. /* execute a current shell command */
  89.  
  90. int execcursh(cmd)        /**/
  91. Cmd cmd;
  92. {
  93.     runlist(cmd->u.list);
  94.     cmd->u.list = NULL;
  95.     return lastval;
  96. }
  97.  
  98. /* execve after handling $_ and #! */
  99.  
  100. #define POUNDBANGLIMIT 64
  101.  
  102. int zexecve(pth, argv)        /**/
  103. char *pth;
  104. char **argv;
  105. {
  106.     int eno;
  107.     static char buf[MAXPATHLEN * 2];
  108.     char **eep;
  109.  
  110.     for (eep = environ; *eep; eep++)
  111.     if (**eep == '_' && (*eep)[1] == '=')
  112.         break;
  113.     buf[0] = '_';
  114.     buf[1] = '=';
  115.     if (*pth == '/')
  116.     strcpy(buf + 2, pth);
  117.     else
  118.     sprintf(buf + 2, "%s/%s", pwd, pth);
  119.     if (!*eep)
  120.     eep[1] = NULL;
  121.     *eep = buf;
  122.     execve(pth, argv, environ);
  123.     if ((eno = errno) == ENOEXEC) {
  124.     char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0;
  125.     int fd, ct, t0;
  126.  
  127.     if ((fd = open(pth, O_RDONLY)) >= 0) {
  128.         argv0 = *argv;
  129.         *argv = pth;
  130.         ct = read(fd, execvebuf, POUNDBANGLIMIT);
  131.         close(fd);
  132.         if (ct > 0) {
  133.         if (execvebuf[0] == '#')
  134.             if (execvebuf[1] == '!') {
  135.             for (t0 = 0; t0 != ct; t0++)
  136.                 if (execvebuf[t0] == '\n')
  137.                 execvebuf[t0] = '\0';
  138.             execvebuf[POUNDBANGLIMIT] = '\0';
  139.             for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
  140.             for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
  141.             if (*ptr) {
  142.                 *ptr = '\0';
  143.                 argv[-2] = ptr2;
  144.                 argv[-1] = ptr + 1;
  145.                 execve(ptr2, argv - 2, environ);
  146.             } else {
  147.                 argv[-1] = ptr2;
  148.                 execve(ptr2, argv - 1, environ);
  149.             }
  150.             } else {
  151.             argv[-1] = "sh";
  152.             execve("/bin/sh", argv - 1, environ);
  153.         } else {
  154.             for (t0 = 0; t0 != ct; t0++)
  155.             if (!execvebuf[t0])
  156.                 break;
  157.             if (t0 == ct) {
  158.             argv[-1] = "sh";
  159.             execve("/bin/sh", argv - 1, environ);
  160.             }
  161.         }
  162.         } else
  163.         eno = errno;
  164.         *argv = argv0;
  165.     } else
  166.         eno = errno;
  167.     }
  168.     return eno;
  169. }
  170.  
  171. #define MAXCMDLEN (MAXPATHLEN*4)
  172.  
  173. /* execute an external command */
  174.  
  175. void execute(dash)        /**/
  176. int dash;
  177. {
  178.     static Lklist exargs;
  179.     char **argv, *arg0, **pp;
  180.     char *z, *s, buf[MAXCMDLEN], buf2[MAXCMDLEN];
  181.     int ee, eno = 0;
  182.     Cmdnam cn;
  183.  
  184.     if (empty(args)) {
  185.     zerr("no command", NULL, 0);
  186.     _exit(1);
  187.     }
  188.     if (!exargs && (s = zgetenv("STTY"))) {
  189.     exargs = args;
  190.     args = NULL;
  191.     zyztem("stty", s);
  192.     args = exargs;
  193.     exargs = NULL;
  194.     }
  195.     arg0 = (char *)peekfirst(args);
  196.     cn = (Cmdnam) gethnode(arg0, cmdnamtab);
  197.     if (cn && (cn->flags & DISABLED))
  198.     cn = NULL;
  199.     if ((z = zgetenv("ARGV0"))) {
  200.     setdata(firstnode(args), (vptr) ztrdup(z));
  201.     delenv(z - 6);
  202.     } else if (dash) {
  203.     sprintf(buf2, "-%s", arg0);
  204.     setdata(firstnode(args), (vptr) ztrdup(buf2));
  205.     }
  206.     argv = makecline(args);
  207.     unblockchld();
  208.     if ((int) strlen(arg0) > MAXPATHLEN) {
  209.     zerr("command too long: %s", arg0, 0);
  210.     _exit(1);
  211.     }
  212.     for (s = arg0; *s; s++)
  213.     if (*s == '/') {
  214.         errno = zexecve(arg0, argv);
  215.         if (arg0 == s || unset(PATHDIRS) ||
  216.         (arg0[0] == '.' && (arg0 + 1 == s ||
  217.                     (arg0[1] == '.' && arg0 + 2 == s)))) {
  218.         zerr("%e: %s", arg0, errno);
  219.         _exit(1);
  220.         }
  221.         break;
  222.     }
  223.     if (cn && ISEXCMD(cn->flags)) {
  224.     char nn[MAXPATHLEN];
  225.  
  226.     if (cn->flags & BUILTIN)
  227.         strcpy(nn, cn->u.cmd);
  228.     else {
  229.         for (pp = path; pp < cn->u.name; pp++)
  230.         if (**pp == '.' && (*pp)[1] == '\0') {
  231.             ee = zexecve(arg0, argv);
  232.             if (ee != ENOENT)
  233.             eno = ee;
  234.         } else if (**pp != '/') {
  235.             z = buf;
  236.             strucpy(&z, *pp);
  237.             *z++ = '/';
  238.             strcpy(z, arg0);
  239.             ee = zexecve(buf, argv);
  240.             if (ee != ENOENT)
  241.             eno = ee;
  242.         }
  243.         strcpy(nn, cn->u.name ? *(cn->u.name) : "");
  244.         strcat(nn, "/");
  245.         strcat(nn, cn->nam);
  246.     }
  247.     ee = zexecve(nn, argv);
  248.  
  249.     if (ee != ENOENT)
  250.         eno = ee;
  251.     }
  252.     for (pp = path; *pp; pp++)
  253.     if ((*pp)[0] == '.' && !(*pp)[1]) {
  254.         ee = zexecve(arg0, argv);
  255.         if (ee != ENOENT)
  256.         eno = ee;
  257.     } else {
  258.         z = buf;
  259.         strucpy(&z, *pp);
  260.         *z++ = '/';
  261.         strcpy(z, arg0);
  262.         ee = zexecve(buf, argv);
  263.         if (ee != ENOENT)
  264.         eno = ee;
  265.     }
  266.     if (eno)
  267.     zerr("%e: %s", arg0, eno);
  268.     else
  269.     zerr("command not found: %s", arg0, 0);
  270.     _exit(1);
  271. }
  272.  
  273. #define try(X) { if (iscom(X)) return ztrdup(X); }
  274.  
  275. /* get the full pathname of an external command */
  276.  
  277. char *findcmd(arg0)        /**/
  278. char *arg0;
  279. {
  280.     char **pp;
  281.     char *z, *s, buf[MAXCMDLEN];
  282.     Cmdnam cn;
  283.  
  284.     cn = (Cmdnam) gethnode(arg0, cmdnamtab);
  285.     if (!cn && isset(HASHCMDS))
  286.     cn = hashcmd(arg0, path);
  287.     if (cn && cn->flags & DISABLED)
  288.     cn = NULL;
  289.     if ((int) strlen(arg0) > MAXPATHLEN)
  290.     return NULL;
  291.     for (s = arg0; *s; s++)
  292.     if (*s == '/') {
  293.         try(arg0);
  294.         if (arg0 == s || unset(PATHDIRS)) {
  295.         return NULL;
  296.         }
  297.         break;
  298.     }
  299.     if (cn && ISEXCMD(cn->flags)) {
  300.     char nn[MAXPATHLEN];
  301.  
  302.     if (cn->flags & BUILTIN)
  303.         strcpy(nn, cn->u.cmd);
  304.     else {
  305.         for (pp = path; pp < cn->u.name; pp++)
  306.         if (**pp != '/') {
  307.             z = buf;
  308.             strucpy(&z, *pp);
  309.             *z++ = '/';
  310.             strcpy(z, arg0);
  311.             try(buf);
  312.         }
  313.  
  314.         strcpy(nn, cn->u.name ? *(cn->u.name) : "");
  315.         strcat(nn, "/");
  316.         strcat(nn, cn->nam);
  317.     }
  318.     try(nn);
  319.     }
  320.     for (pp = path; *pp; pp++) {
  321.     z = buf;
  322.     strucpy(&z, *pp);
  323.     *z++ = '/';
  324.     strcpy(z, arg0);
  325.     try(buf);
  326.     }
  327.     return NULL;
  328. }
  329.  
  330. int iscom(s)            /**/
  331. char *s;
  332. {
  333.     struct stat statbuf;
  334.  
  335.     return (access(s, X_OK) == 0 && stat(s, &statbuf) >= 0 &&
  336.         S_ISREG(statbuf.st_mode));
  337. }
  338.  
  339. int isrelative(s)        /**/
  340. char *s;
  341. {
  342.     if (*s != '/')
  343.     return 1;
  344.     for (; *s; s++)
  345.     if (*s == '.' && s[-1] == '/' &&
  346.         (s[1] == '/' || s[1] == '\0' ||
  347.          (s[1] == '.' && (s[2] == '/' || s[2] == '\0'))))
  348.         return 1;
  349.     return 0;
  350. }
  351.  
  352. Cmdnam hashcmd(arg0, pp)    /**/
  353. char *arg0;
  354. char **pp;
  355. {
  356.     char *s, buf[MAXPATHLEN];
  357.     char **pq;
  358.     DIR *dir;
  359.     struct dirent *de;
  360.     Cmdnam cn;
  361.  
  362.     for (; *pp; pp++)
  363.     if (**pp == '/') {
  364.         s = buf;
  365.         strucpy(&s, *pp);
  366.         *s++ = '/';
  367.         strcpy(s, arg0);
  368.         if (iscom(buf))
  369.         break;
  370.     }
  371.     if (!*pp || isrelative(*pp))
  372.     return NULL;
  373.     cn = (Cmdnam) zcalloc(sizeof *cn);
  374.     cn->flags = EXCMD;
  375.     cn->u.name = pp;
  376.     addhnode(ztrdup(arg0), cn, cmdnamtab, freecmdnam);
  377.     if (unset(HASHDIRS))
  378.     return cn;
  379.     for (pq = pathchecked; pq <= pp; pq++) {
  380.     if (isrelative(*pq) || !(dir = opendir(*pq)))
  381.         continue;
  382.     readdir(dir);
  383.     readdir(dir);
  384.     while ((de = readdir(dir)))
  385.         addhcmdnode(de->d_name, pq);
  386.     closedir(dir);
  387.     }
  388.     pathchecked = pp + 1;
  389.     return cn;
  390. }
  391.  
  392. void fullhash()
  393. {                /**/
  394.     char **pq;
  395.     DIR *dir;
  396.     struct dirent *de;
  397.  
  398.     for (pq = pathchecked; *pq; pq++) {
  399.     if (isrelative(*pq) || !(dir = opendir(*pq)))
  400.         continue;
  401.     readdir(dir);
  402.     readdir(dir);
  403.     while ((de = readdir(dir)))
  404.         addhcmdnode(de->d_name, pq);
  405.     closedir(dir);
  406.     }
  407.     pathchecked = pq;
  408. }
  409.  
  410. void execlist(list)        /**/
  411. List list;
  412. {
  413.     if (breaks)
  414.     return;
  415.     if (!list || list == &dummy_list)
  416.     return;
  417.     simplifyright(list);
  418.     switch (list->type) {
  419.     case SYNC:
  420.     case ASYNC:
  421.     execlist2(list->left, list->type, !list->right);
  422.     if (sigtrapped[SIGDEBUG])
  423.         dotrap(SIGDEBUG);
  424.     if (sigtrapped[SIGZERR] && lastval)
  425.         dotrap(SIGZERR);
  426.     if (lastval && isset(ERREXIT) && sourcelevel < 32768) {
  427.         if (sigtrapped[SIGEXIT])
  428.         dotrap(SIGEXIT);
  429.         exit(lastval);
  430.     }
  431.     if (list->right && !retflag) {
  432.     /* errflag = 0; */
  433.         execlist(list->right);
  434.     }
  435.     break;
  436.     }
  437. }
  438.  
  439. void execlist2(list, type, last1)    /**/
  440. Sublist list;
  441. int type;
  442. int last1;
  443. {
  444.     if (!list)
  445.     return;
  446.     switch (list->type) {
  447.     case END:
  448.     execpline(list, type, last1);
  449.     break;
  450.     case ORNEXT:
  451.     if (!execpline(list, SYNC, 0))
  452.         execlist2(list->right, type, last1);
  453.     else
  454.         while ((list = list->right))
  455.         if (list->type == ANDNEXT) {
  456.             execlist2(list->right, type, last1);
  457.             return;
  458.         }
  459.     break;
  460.     case ANDNEXT:
  461.     if (execpline(list, SYNC, 0))
  462.         execlist2(list->right, type, last1);
  463.     else
  464.         while ((list = list->right))
  465.         if (list->type == ORNEXT) {
  466.             execlist2(list->right, type, last1);
  467.             return;
  468.         }
  469.     break;
  470.     }
  471. }
  472.  
  473. static int in_pipe;
  474.  
  475. int execpline(l, how, last1)    /**/
  476. Sublist l;
  477. int how;
  478. int last1;
  479. {
  480.     int ipipe[2], opipe[2];
  481.  
  482.     if (!l)
  483.     return 0;
  484.     ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
  485.     if (in_pipe) {
  486.     execpline2(l->left, how, opipe[0], ipipe[1], last1);
  487.     return lastval;    /* if we're in a pipe don't change lastval; it'll be */
  488.     }            /* done by the first instance of execpline */
  489.     blockchld();
  490.     if ((thisjob = getfreejob()) == -1)
  491.     return 1;
  492.     initjob();
  493.     if (how == TIMED) {
  494.     jobtab[thisjob].stat |= STAT_TIMED;
  495.     how = SYNC;
  496.     }
  497.     if (l->flags & PFLAG_COPROC) {
  498.     how = ASYNC;
  499.     if (coprocin >= 0) {
  500.         close(coprocin);
  501.         close(coprocout);
  502.     }
  503.     mpipe(ipipe);
  504.     mpipe(opipe);
  505.     coprocin = ipipe[0];
  506.     coprocout = opipe[1];
  507.     }
  508.     execpline2(l->left, how, opipe[0], ipipe[1], last1);
  509.     if (how == ASYNC) {
  510.     if (l->flags & PFLAG_COPROC)
  511.         close(ipipe[1]);
  512.     spawnjob();
  513.     unblockchld();
  514.     return 1;
  515.     } else {
  516.     waitjobs();
  517.     unblockchld();
  518.     if (l->flags & PFLAG_NOT)
  519.         lastval = !lastval;
  520.     return !lastval;
  521.     }
  522. }
  523.  
  524. void execpline2(pline, how, input, output, last1)    /**/
  525. Pline pline;
  526. int how;
  527. int input;
  528. int output;
  529. int last1;
  530. {
  531.     int pid;
  532.     int pipes[2];
  533.  
  534.     if (breaks)
  535.     return;
  536.     if (!pline)
  537.     return;
  538.     if (pline->type == END) {
  539.     in_pipe = 0;
  540.     execcmd(pline->left, input, output, how == ASYNC, last1);
  541.     pline->left = NULL;
  542.     } else {
  543.     int old_pipe = in_pipe;
  544.     in_pipe = 0;
  545.     mpipe(pipes);
  546.     if (pline->left->type >= CURSH && how == SYNC) {
  547.  
  548.     /* if we are doing "foo | bar" where foo is a current
  549.                 shell command, do foo in a subshell and do
  550.                 the rest of the pipeline in the current shell. */
  551.  
  552.         int synch[2];
  553.  
  554.         pipe(synch);
  555.         if (!(pid = fork())) {
  556.         close(pipes[0]);
  557.         close(synch[0]);
  558.         entersubsh(how == ASYNC);
  559.         close(synch[1]);
  560.         exiting = 1;
  561.         execcmd(pline->left, input, pipes[1], how == ASYNC, 0);
  562.         _exit(lastval);
  563.         } else if (pid == -1) {
  564.         close(synch[0]);
  565.         close(synch[1]);
  566.         zerr("fork failed: %e", NULL, errno);
  567.         } else {
  568.         char dummy, *text = getjobtext((vptr) pline->left);
  569.  
  570.         addproc(pid, text);
  571.         close(synch[1]);
  572.         read(synch[0], &dummy, 1);
  573.         close(synch[0]);
  574.         }
  575.     } else {
  576.     /* otherwise just do the pipeline normally. */
  577.         execcmd(pline->left, input, pipes[1], how == ASYNC, 0);
  578.     }
  579.     in_pipe = old_pipe;
  580.     pline->left = NULL;
  581.     close(pipes[1]);
  582.     if (pline->right) {
  583.     /* if another execpline() is invoked because the command is a list it
  584.        must know that we're already in a pipeline */
  585.         in_pipe = 1;
  586.         execpline2(pline->right, how, pipes[0], output, last1);
  587.         close(pipes[0]);
  588.     }
  589.     }
  590. }
  591.  
  592. /* make the argv array */
  593.  
  594. char **makecline(list)        /**/
  595. struct lklist *list;
  596. {
  597.     int ct = 0;
  598.     Lknode node;
  599.     char **argv, **ptr;
  600.  
  601.     if (isset(XTRACE)) {
  602.     fprintf(stderr, "%s", (prompt4) ? prompt4 : "");
  603.     for (node = firstnode(list); node; incnode(node), ct++);
  604.     ptr = argv = 2 + (char **)ncalloc((ct + 4) * sizeof(char *));
  605.  
  606.     for (node = firstnode(list); node; incnode(node))
  607.         if (*(char *)getdata(node)) {
  608.         *ptr++ = (char *)getdata(node);
  609.         untokenize(getdata(node));
  610.         fputs(getdata(node), stderr);
  611.         if (nextnode(node))
  612.             fputc(' ', stderr);
  613.         }
  614.     *ptr = NULL;
  615.     fputc('\n', stderr);
  616.     fflush(stderr);
  617.     return (argv);
  618.     } else {
  619.     for (node = firstnode(list); node; incnode(node), ct++);
  620.     ptr = argv = 2 + (char **)ncalloc((ct + 4) * sizeof(char *));
  621.  
  622.     for (node = firstnode(list); node; incnode(node))
  623.         if (*(char *)getdata(node)) {
  624.         *ptr++ = (char *)getdata(node);
  625.         untokenize(getdata(node));
  626.         }
  627.     *ptr = NULL;
  628.     return (argv);
  629.     }
  630. }
  631.  
  632. /* untokenize the command line and remove null arguments */
  633.  
  634. void fixcline(l)        /**/
  635. Lklist l;
  636. {
  637.     Lknode node, next;
  638.  
  639.     for (node = firstnode(l); node; node = next) {
  640.     next = nextnode(node);
  641.     if (!*(char *)getdata(node))
  642.         uremnode(l, node);
  643.     else
  644.         untokenize(getdata(node));
  645.     }
  646. }
  647.  
  648. void untokenize(s)        /**/
  649. char *s;
  650. {
  651.     for (; *s; s++)
  652.     if (itok(*s))
  653.         if (*s == Nularg)
  654.         chuck(s--);
  655.         else
  656.         *s = ztokens[*s - Pound];
  657. }
  658.  
  659. /* nonzero if we shouldn't clobber a file */
  660.  
  661. int dontclob(f)            /**/
  662. struct redir *f;
  663. {
  664.     struct stat buf;
  665.  
  666.     if (unset(NOCLOBBER) || f->type & 1)
  667.     return 0;
  668.     if (stat(f->name, &buf) == -1)
  669.     return 1;
  670.     return S_ISREG(buf.st_mode);
  671. }
  672.  
  673. /* close an multio (success) */
  674.  
  675. void closemn(mfds, fd)        /**/
  676. struct multio **mfds;
  677. int fd;
  678. {
  679.     if (mfds[fd]) {
  680.     if (mfds[fd]->ct > 1)
  681.         if (mfds[fd]->rflag == 0)
  682.         catproc(mfds[fd]);
  683.         else
  684.         teeproc(mfds[fd]);
  685.     mfds[fd] = NULL;
  686.     }
  687. }
  688.  
  689. /* close all the mnodes (failure) */
  690.  
  691. void closemnodes(mfds)        /**/
  692. struct multio **mfds;
  693. {
  694.     int t0, t1;
  695.  
  696.     for (t0 = 0; t0 != 10; t0++)
  697.     if (mfds[t0]) {
  698.         for (t1 = 0; t1 != mfds[t0]->ct; t1++)
  699.         close(mfds[t0]->fds[t1]);
  700.         mfds[t0] = NULL;
  701.     }
  702. }
  703.  
  704. /* add a fd to an multio */
  705. /* an multio is a list of fds associated with a certain fd.
  706.     thus if you do "foo >bar >ble", the multio for fd 1 will have
  707.     two fds, the result of open("bar",...), and the result of
  708.     open("ble",....). */
  709.  
  710. void addfd(forked, save, mfds, fd1, fd2, rflag)    /**/
  711. int forked;
  712. int *save;
  713. struct multio **mfds;
  714. int fd1;
  715. int fd2;
  716. int rflag;
  717. {
  718.     int pipes[2];
  719.  
  720.     if (!mfds[fd1]) {        /* starting a new multio */
  721.     mfds[fd1] = (struct multio *)alloc(sizeof(struct multio));
  722.  
  723.     if (!forked && fd1 != fd2 && fd1 < 10)
  724.         save[fd1] = movefd(fd1);
  725.     redup(fd2, fd1);
  726.     mfds[fd1]->ct = 1;
  727.     mfds[fd1]->fds[0] = fd1;
  728.     mfds[fd1]->rflag = rflag;
  729.     } else {
  730.     if (mfds[fd1]->rflag != rflag) {
  731.         zerr("file mode mismatch on fd %d", NULL, fd1);
  732.         return;
  733.     }
  734.     if (mfds[fd1]->ct == 1) {    /* split the stream */
  735.         mfds[fd1]->fds[0] = movefd(fd1);
  736.         mfds[fd1]->fds[1] = movefd(fd2);
  737.         mpipe(pipes);
  738.         mfds[fd1]->pipe = pipes[1 - rflag];
  739.         redup(pipes[rflag], fd1);
  740.         mfds[fd1]->ct = 2;
  741.     } else            /* add another fd to an already split stream */
  742.         mfds[fd1]->fds[mfds[fd1]->ct++] = movefd(fd2);
  743.     }
  744. }
  745.  
  746. void addvars(l, export)        /**/
  747. Lklist l;
  748. int export;
  749. {
  750.     struct varasg *v;
  751.     Lklist vl;
  752.  
  753.     while (full(l)) {
  754.     char **arr, **ptr;
  755.  
  756.     v = (struct varasg *)ugetnode(l);
  757.     singsub(&v->name);
  758.     if (errflag)
  759.         return;
  760.     untokenize(v->name);
  761.     if (v->type == PMFLAG_s) {
  762.         vl = newlist();
  763.         addnode(vl, v->str);
  764.     } else
  765.         vl = v->arr;
  766.     prefork(vl, v->type ? 3 : 013);
  767.     if (errflag)
  768.         return;
  769.     postfork(vl, v->type ? 1 : 011);
  770.     if (errflag)
  771.         return;
  772.     if (v->type == PMFLAG_s && (empty(vl) || !nextnode(firstnode(vl)))) {
  773.         Param pm;
  774.         char *val;
  775.  
  776.         if (empty(vl))
  777.         val = ztrdup("");
  778.         else {
  779.         untokenize(peekfirst(vl));
  780.         val = ztrdup(ugetnode(vl));
  781.         }
  782.         pm = setsparam(v->name, ztrdup(val));
  783.         if (errflag) return;
  784.         if (export && !(pm->flags & PMFLAG_x))
  785.         addenv(v->name, val);
  786.         zsfree(val);
  787.         continue;
  788.     }
  789.     ptr = arr = (char **)zalloc(sizeof(char **) * (countnodes(vl) + 1));
  790.  
  791.     while (full(vl)) {
  792.         char *pp;
  793.  
  794.         pp = (char *)ugetnode(vl);
  795.         if (*pp) {
  796.         *ptr = ztrdup(pp);
  797.         untokenize(*ptr++);
  798.         }
  799.     }
  800.     *ptr = NULL;
  801.     setaparam(v->name, arr);
  802.     if (errflag) return;
  803.     }
  804. }
  805.  
  806. void execcmd(cmd, input, output, bkg, last1)    /**/
  807. Cmd cmd;
  808. int input;
  809. int output;
  810. int bkg;
  811. int last1;
  812. {
  813.     int type;
  814.     long pid;
  815.     int save[10], t0;
  816.     struct redir *fn;
  817.     struct multio *mfds[10];
  818.     int fil, forked = 0, iscursh, nullexec = 0, assign = 0;
  819.     char *text, dummy;
  820.     Cmdnam cn1 = NULL;
  821.  
  822.     args = cmd->args;
  823.     for (t0 = 0; t0 != 10; t0++) {
  824.     save[t0] = -1;
  825.     mfds[t0] = NULL;
  826.     }
  827.     if ((type = cmd->type) == SIMPLE && empty(args))
  828.     if (full(cmd->redir))
  829.         if (cmd->flags & CFLAG_EXEC) {
  830.         nullexec = 1;
  831.         } else if (!*nullcmd) {
  832.         zerr("redirection with no command", NULL, 0);
  833.         errflag = lastval = 1;
  834.         return;
  835.         } else if (*readnullcmd &&
  836.                ((Redir) peekfirst(cmd->redir))->type == READ &&
  837.                !nextnode(firstnode(cmd->redir))) {
  838.         addnode(args, dupstring(readnullcmd));
  839.         } else
  840.         addnode(args, dupstring(nullcmd));
  841.     else {
  842.         addvars(cmd->vars, 0);
  843.         if (errflag)
  844.         lastval = 1;
  845.         return;
  846.     }
  847.     if (full(args) && *(char *)peekfirst(args) == '%') {
  848.     insnode(args, (Lknode) args, dupstring((bkg) ? "bg" : "fg"));
  849.     bkg = 0;
  850.     }
  851.     if (isset(AUTORESUME) && !bkg && empty(cmd->redir) && full(args) &&
  852.     !input && type == SIMPLE && !nextnode(firstnode(args))) {
  853.     if (unset(NOTIFY))
  854.         scanjobs();
  855.     if (findjobnam(peekfirst(args)) != -1)
  856.         pushnode(args, dupstring("fg"));
  857.     }
  858.     if (jobbing) {        /* get the text associated with this command */
  859.     text = getjobtext((vptr) cmd);
  860.     } else
  861.     text = NULL;
  862.     if (full(args) && !(cmd->flags & CFLAG_COMMAND) && type == SIMPLE) {
  863.     char *cmdarg = (char *)peekfirst(args);
  864.     Cmdnam cn2 = NULL;
  865.     int builtin = 0;
  866.  
  867.     if (!strcmp(cmdarg, "builtin") && args->first->next &&
  868.         (cn2 = (Cmdnam) gethnode(cmdarg, cmdnamtab)) &&
  869.         (cn2->flags & BUILTIN) &&
  870.         !(cn2->flags & EXCMD)) {
  871.         builtin = 1;
  872.         cmdarg = (char *)args->first->next->dat;
  873.     }
  874.     cn1 = (Cmdnam) gethnode(cmdarg, cmdnamtab);
  875.     if (cn1 && (cn1->flags & BUILTIN) && !(cn1->flags & EXCMD))
  876.         assign = istypeset(cn1, NULL);
  877.     else if (builtin)
  878.         assign = istypeset(NULL, cmdarg);
  879.     if (builtin)
  880.         cn1 = cn2;
  881.     }
  882.  
  883.     /* do prefork substitutions */
  884.     prefork(args, (((type == CCASE) ? 010 : 0)
  885.            | (assign ? 02 : isset(MAGICEQUALSUBST))));
  886.  
  887.     /* warn about "rm *" */
  888.     if (unset(RMSTARSILENT) && interact && isset(SHINSTDIN) &&
  889.     type == SIMPLE && full(args) && nextnode(firstnode(args)) &&
  890.     !strcmp(peekfirst(args), "rm") &&
  891.     !(cmd->flags & CFLAG_NOGLOB)) {
  892.     Lknode node, next;
  893.  
  894.     for (node = nextnode(firstnode(args)); node && !errflag; node = next) {
  895.         char *s = (char *)getdata(node);
  896.         int l = strlen(s);
  897.  
  898.         next = nextnode(node);
  899.         if (s[0] == Star && !s[1]) {
  900.         if (!checkrmall(pwd))
  901.             uremnode(args, node);
  902.         } else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) {
  903.         char t = s[l - 2];
  904.  
  905.         s[l - 2] = 0;
  906.         if (!checkrmall(s))
  907.             uremnode(args, node);
  908.         s[l - 2] = t;
  909.         }
  910.     }
  911.     if (!nextnode(firstnode(args)))
  912.         errflag = 1;
  913.     }
  914.     if (errflag) {
  915.     lastval = 1;
  916.     return;
  917.     }
  918.     if (!cn1) {
  919.     char *s, *t;
  920.  
  921.     if (full(args) && ((char *)peekfirst(args))[0] == Inbrack &&
  922.         ((char *)peekfirst(args))[1] == '\0')
  923.         ((char *)peekfirst(args))[0] = '[';
  924.     if (type == SIMPLE && full(args) && !(cmd->flags & CFLAG_COMMAND)) {
  925.         cn1 = (Cmdnam) gethnode(t = s = (char *)peekfirst(args), cmdnamtab);
  926.         if (!cn1 && isset(HASHCMDS) && strcmp(t, "..")) {
  927.         while (*t && *t != '/')
  928.             t++;
  929.         if (!*t)
  930.             cn1 = hashcmd(s, pathchecked);
  931.         }
  932.     }
  933.     if (type == SIMPLE && !cn1 && isset(AUTOCD) && isset(SHINSTDIN) &&
  934.         full(args) && empty(cmd->redir) &&
  935.         !nextnode(firstnode(args)) && (s = cancd(peekfirst(args)))) {
  936.         peekfirst(args) = (vptr) s;
  937.         pushnode(args, dupstring("cd"));
  938.         cn1 = (Cmdnam) gethnode("cd", cmdnamtab);
  939.     }
  940.     }
  941. /* this is nonzero if cmd is a current shell procedure */
  942.  
  943.     iscursh = (type >= CURSH) || (type == SIMPLE && cn1 &&
  944.                   (cn1->flags & (BUILTIN | SHFUNC)) &&
  945.                   !(cn1->flags & EXCMD));
  946.  
  947. /* if this command is backgrounded or (this is an external
  948.         command and we are not exec'ing it) or this is a builtin
  949.         with output piped somewhere, then fork.  If this is the
  950.         last stage in a subshell pipeline, don't fork, but make
  951.         the rest of the function think we forked. */
  952.  
  953.     if (bkg || !(iscursh || (cmd->flags & CFLAG_EXEC)) ||
  954.     (cn1 && (cn1->flags & (BUILTIN | SHFUNC)) && 
  955.      !(cn1->flags & EXCMD) && output)) {
  956.     int synch[2];
  957.  
  958.     pipe(synch);
  959.     pid = (last1 && execok())? 0 : phork();
  960.     if (pid == -1) {
  961.         close(synch[0]);
  962.         close(synch[1]);
  963.         return;
  964.     }
  965.     if (pid) {
  966.         close(synch[1]);
  967.         read(synch[0], &dummy, 1);
  968.         close(synch[0]);
  969.         if (pid == -1)
  970.         zerr("%e", NULL, errno);
  971.         else {
  972.         if (bkg)
  973.             lastpid = pid;
  974.         else if (!jobtab[thisjob].stty_in_env && full(cmd->vars))
  975.             while (full(cmd->vars))    /* search for STTY=... */
  976.             if (!strcmp(((Varasg)ugetnode(cmd->vars))->name,
  977.                     "STTY")) { 
  978.                 jobtab[thisjob].stty_in_env = 1;
  979.                 break;
  980.             }
  981.         addproc(pid, text);
  982.         }
  983.         return;
  984.     }
  985.     close(synch[0]);
  986.     entersubsh(bkg);
  987.     close(synch[1]);
  988.     forked = 1;
  989.     }
  990.     if (bkg && isset(BGNICE))
  991.     nice(5);
  992.  
  993. /* perform postfork substitutions */
  994.     postfork(args, !(cmd->flags & CFLAG_NOGLOB));
  995.     if (errflag) {
  996.     lastval = 1;
  997.     goto err;
  998.     } else {
  999.     char *s;
  1000.  
  1001.     while (full(args) && (s = (char *)peekfirst(args)) && !*s)
  1002.         ugetnode(args);
  1003.     }
  1004.  
  1005.     if (input)            /* add pipeline input/output to mnodes */
  1006.     addfd(forked, save, mfds, 0, input, 0);
  1007.     if (output)
  1008.     addfd(forked, save, mfds, 1, output, 1);
  1009.     spawnpipes(cmd->redir);    /* do process substitutions */
  1010.     while (full(cmd->redir))
  1011.     if ((fn = (struct redir *)ugetnode(cmd->redir))->type == INPIPE) {
  1012.         if (fn->fd2 == -1) {
  1013.         fixfds(save);
  1014.         execerr();
  1015.         }
  1016.         addfd(forked, save, mfds, fn->fd1, fn->fd2, 0);
  1017.     } else if (fn->type == OUTPIPE) {
  1018.         if (fn->fd2 == -1) {
  1019.         fixfds(save);
  1020.         execerr();
  1021.         }
  1022.         addfd(forked, save, mfds, fn->fd1, fn->fd2, 1);
  1023.     } else {
  1024.         if (!(fn->type == HERESTR || fn->type == CLOSE || fn->type ==
  1025.           MERGE || fn->type == MERGEOUT))
  1026.         if (xpandredir(fn, cmd->redir))
  1027.             continue;
  1028.         if (errflag) {
  1029.         fixfds(save);
  1030.         execerr();
  1031.         }
  1032.         if (fn->type == HERESTR) {
  1033.         fil = getherestr(fn);
  1034.         if (fil == -1) {
  1035.             fixfds(save);
  1036.             if (errno != EINTR)
  1037.             zerr("%e", NULL, errno);
  1038.             execerr();
  1039.         }
  1040.         addfd(forked, save, mfds, fn->fd1, fil, 0);
  1041.         } else if (fn->type == READ) {
  1042.         fil = open(fn->name, O_RDONLY);
  1043.         if (fil == -1) {
  1044.             fixfds(save);
  1045.             if (errno != EINTR)
  1046.             zerr("%e: %s", fn->name, errno);
  1047.             execerr();
  1048.         }
  1049.         addfd(forked, save, mfds, fn->fd1, fil, 0);
  1050.         } else if (fn->type == CLOSE) {
  1051.         if (!forked && fn->fd1 < 10)
  1052.             save[fn->fd1] = movefd(fn->fd1);
  1053.         closemn(mfds, fn->fd1);
  1054.         close(fn->fd1);
  1055.         } else if (fn->type == MERGE || fn->type == MERGEOUT) {
  1056.         if (fn->fd2 == FD_COPROC)
  1057.             fn->fd2 = (fn->type == MERGEOUT) ? coprocout : coprocin;
  1058.         closemn(mfds, fn->fd1);
  1059.         fil = dup(fn->fd2);
  1060.         if (fil == -1) {
  1061.             char fdstr[4];
  1062.  
  1063.             fixfds(save);
  1064.             sprintf(fdstr, "%d", fn->fd2);
  1065.             zerr("%s: %e", fdstr, errno);
  1066.             execerr();
  1067.         }
  1068.         addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT);
  1069.         } else {
  1070.         if (fn->type >= APP)
  1071.             fil = open(fn->name,
  1072.                    (isset(NOCLOBBER) && !(fn->type & 1)) ?
  1073.                    O_WRONLY | O_APPEND : O_WRONLY | O_APPEND | O_CREAT, 0666);
  1074.         else
  1075.             fil = open(fn->name, dontclob(fn) ?
  1076.                    O_WRONLY | O_CREAT | O_EXCL : O_WRONLY | O_CREAT | O_TRUNC, 0666);
  1077.         if (fil == -1) {
  1078.             fixfds(save);
  1079.             if (errno != EINTR)
  1080.             zerr("%e: %s", fn->name, errno);
  1081.             execerr();
  1082.         }
  1083.         addfd(forked, save, mfds, fn->fd1, fil, 1);
  1084.         }
  1085.     }
  1086.  
  1087. /* we are done with redirection.  close the mnodes, spawning
  1088.         tee/cat processes as necessary. */
  1089.     for (t0 = 0; t0 != 10; t0++)
  1090.     closemn(mfds, t0);
  1091.  
  1092.     if (nullexec) {
  1093.     for (t0 = 0; t0 != 10; t0++)
  1094.         if (save[t0] != -1)
  1095.         close(save[t0]);
  1096.     return;
  1097.     }
  1098.     if (unset(NOEXEC))
  1099.     if (type >= CURSH) {
  1100.         static int (*func[]) DCLPROTO((Cmd)) =
  1101.         {
  1102.         execcursh, exectime, execfuncdef, execfor, execwhile,
  1103.         execrepeat, execif, execcase, execselect, execcond};
  1104.  
  1105.         fixcline(args);
  1106.         lastval = (func[type - CURSH]) (cmd);
  1107.     } else if (iscursh) {    /* builtin or shell function */
  1108.         if (!cn1) {
  1109.         lastval = 1;
  1110.         return;
  1111.         }
  1112.         if (cmd->vars) {
  1113.         addvars(cmd->vars, 0);
  1114.         if (errflag) {
  1115.             lastval = 1;
  1116.             return;
  1117.         }
  1118.         }
  1119.         fixcline(args);
  1120.         if (cn1->flags & SHFUNC)
  1121.         execshfunc(cmd, cn1);
  1122.         else {
  1123.         if (forked)
  1124.             closem();
  1125.         lastval = execbin(args, cn1);
  1126.         if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
  1127.             lastval && !subsh) {
  1128.             fprintf(stderr, "zsh: exit %ld\n", (long) lastval);
  1129.         }
  1130.         fflush(stdout);
  1131.         if (save[1] == -1) {
  1132.             if (ferror(stdout)) {
  1133.             zerr("write error: %e", NULL, errno);
  1134.             clearerr(stdout);
  1135.             errflag = 0;
  1136.             }
  1137.         }
  1138.         else
  1139.             clearerr(stdout);
  1140.         }
  1141.     } else {
  1142.         if (cmd->flags & CFLAG_EXEC)
  1143.         setiparam("SHLVL", --shlvl);
  1144.         if (cmd->vars) {
  1145.         addvars(cmd->vars, 1);
  1146.         if (errflag) {
  1147.             lastval = 1;
  1148.             return;
  1149.         }
  1150.         }
  1151.         if (type == SIMPLE) {
  1152.         closem();
  1153.         execute(cmd->flags & CFLAG_DASH);
  1154.         } else        /* ( ... ) */
  1155.         execlist(cmd->u.list);
  1156.     }
  1157.   err:
  1158.     if (forked)
  1159.     _exit(lastval);
  1160.     fixfds(save);
  1161. }
  1162.  
  1163. /* restore fds after redirecting a builtin */
  1164.  
  1165. void fixfds(save)        /**/
  1166. int *save;
  1167. {
  1168.     int old_errno = errno;
  1169.     int t0;
  1170.  
  1171.     for (t0 = 0; t0 != 10; t0++)
  1172.     if (save[t0] != -1)
  1173.         redup(save[t0], t0);
  1174.     errno = old_errno;
  1175. }
  1176.  
  1177. void entersubsh(bkg)        /**/
  1178. int bkg;
  1179. {
  1180.     if (!jobbing) {
  1181.     if (bkg) {
  1182.         sigtrapped[SIGINT] = 2;
  1183.         sig_ignore(SIGINT);
  1184.         sigtrapped[SIGQUIT] = 2;
  1185.         sig_ignore(SIGQUIT);
  1186.         if (isatty(0)) {
  1187.         close(0);
  1188.         if (open("/dev/null", O_RDWR)) {
  1189.             zerr("can't open /dev/null: %e", NULL, errno);
  1190.             _exit(1);
  1191.         }
  1192.         }
  1193.     }
  1194.     } else if (thisjob != -1) {
  1195.     if (!jobtab[thisjob].gleader) {
  1196.         jobtab[thisjob].gleader = getpid();
  1197.         setpgrp(0L, jobtab[thisjob].gleader);
  1198.         if (!bkg)
  1199.         attachtty(jobtab[thisjob].gleader);
  1200.     } else
  1201.         setpgrp(0L, jobtab[thisjob].gleader);
  1202.     }
  1203.     subsh = 1;
  1204.     if (SHTTY != -1) {
  1205.     close(SHTTY);
  1206.     SHTTY = -1;
  1207.     }
  1208.     if (jobbing) {
  1209.     sig_default(SIGTTOU);
  1210.     sig_default(SIGTTIN);
  1211.     sig_default(SIGTSTP);
  1212.     sig_default(SIGPIPE);
  1213.     }
  1214.     if (interact) {
  1215.     sig_default(SIGTERM);
  1216.     if (sigtrapped[SIGINT] != 2)
  1217.         sig_default(SIGINT);
  1218.     }
  1219.     if (sigtrapped[SIGQUIT] != 2)
  1220.     sig_default(SIGQUIT);
  1221.     opts[MONITOR] = OPT_UNSET;
  1222.     clearjobtab();
  1223. }
  1224.  
  1225. /* close all internal shell fds */
  1226.  
  1227. void closem()
  1228. {                /**/
  1229.     int t0;
  1230.  
  1231.     for (t0 = 10; t0 != NOFILE; t0++)
  1232.     close(t0);
  1233. }
  1234.  
  1235. /* convert here document into a here string */
  1236.  
  1237. char *gethere(str, typ)        /**/
  1238. char *str;
  1239. int typ;
  1240. {
  1241.     char pbuf[256];
  1242.     int qt = 0, siz = 0, l, strip = 0;
  1243.     char *s, *t, *bptr;
  1244.  
  1245.     for (s = str; *s; s++)
  1246.     if (INULL(*s)) {
  1247.         *s = Nularg;
  1248.         qt = 1;
  1249.     }
  1250.     untokenize(str);
  1251.     if (typ == HEREDOCDASH) {
  1252.     strip = 1;
  1253.     while (*str == '\t')
  1254.         str++;
  1255.     }
  1256.     t = ztrdup("");
  1257.     for (;;) {
  1258.     char *u, *v;
  1259.  
  1260.     if (!hgets(pbuf, sizeof(pbuf)))
  1261.         break;
  1262.     bptr = pbuf;
  1263.     if (strip)
  1264.         while (*bptr == '\t')
  1265.         bptr++;
  1266.     for (u = bptr, v = str; *u != '\n' && *v; u++, v++)
  1267.         if (*u != *v)
  1268.         break;
  1269.     if (!(*u == '\n' && !*v)) {
  1270.         l = strlen(bptr);
  1271.         if (!qt && l > 1 && bptr[l - 1] == '\n' && bptr[l - 2] == '\\')
  1272.         bptr[l -= 2] = '\0';
  1273.         t = realloc(t, siz + l + 1);
  1274.         strncpy(t + siz, bptr, l);
  1275.         siz += l;
  1276.     } else
  1277.         break;
  1278.     }
  1279.     t[siz] = '\0';
  1280.     if (siz && t[siz - 1] == '\n')
  1281.     t[siz - 1] = '\0';
  1282.     if (!qt)
  1283.     for (s = t; *s; s++)
  1284.         if (*s == '$') {
  1285.         *s = Qstring;
  1286.         } else if (*s == '`') {
  1287.         *s = Qtick;
  1288.         } else if (*s == '(') {
  1289.         *s = Inpar;
  1290.         } else if (*s == ')') {
  1291.         *s = Outpar;
  1292.         } else if (*s == '\\' &&
  1293.                (s[1] == '$' || s[1] == '`'))
  1294.         chuck(s);
  1295.     s = dupstring(t);
  1296.     zsfree(t);
  1297.     return s;
  1298. }
  1299.  
  1300. /* open here string fd */
  1301.  
  1302. int getherestr(fn)        /**/
  1303. struct redir *fn;
  1304. {
  1305.     Lklist fake;
  1306.     char *s = gettemp(), *t;
  1307.     int fd;
  1308.  
  1309.     fake = newlist();
  1310.     addnode(fake, fn->name);
  1311.     prefork(fake, 010);
  1312.     if (!errflag)
  1313.     postfork(fake, 011);
  1314.     if (errflag)
  1315.     return -1;
  1316.     if ((fd = open(s, O_CREAT | O_WRONLY, 0600)) == -1)
  1317.     return -1;
  1318.     while ((t = (char *)ugetnode(fake))) {
  1319.     untokenize(t);
  1320.     write(fd, t, strlen(t));
  1321.     if (full(fake))
  1322.         write(fd, " ", 1);
  1323.     }
  1324.     write(fd, "\n", 1);
  1325.     close(fd);
  1326.     fd = open(s, O_RDONLY);
  1327.     unlink(s);
  1328.     return fd;
  1329. }
  1330.  
  1331. void catproc(mn)        /**/
  1332. struct multio *mn;
  1333. {
  1334.     int len, t0;
  1335.     char *buf;
  1336.  
  1337.     if (phork()) {
  1338.     for (t0 = 0; t0 != mn->ct; t0++)
  1339.         close(mn->fds[t0]);
  1340.     close(mn->pipe);
  1341.     return;
  1342.     }
  1343.     closeallelse(mn);
  1344.     buf = (char *)zalloc(4092);
  1345.     for (t0 = 0; t0 != mn->ct; t0++)
  1346.     while ((len = read(mn->fds[t0], buf, 4092)))
  1347.         write(mn->pipe, buf, len);
  1348.     _exit(0);
  1349. }
  1350.  
  1351. void teeproc(mn)        /**/
  1352. struct multio *mn;
  1353. {
  1354.     int len, t0;
  1355.     char *buf;
  1356.  
  1357.     if (phork()) {
  1358.     for (t0 = 0; t0 != mn->ct; t0++)
  1359.         close(mn->fds[t0]);
  1360.     close(mn->pipe);
  1361.     return;
  1362.     }
  1363.     buf = (char *)zalloc(4092);
  1364.     closeallelse(mn);
  1365.     while ((len = read(mn->pipe, buf, 4092)) > 0)
  1366.     for (t0 = 0; t0 != mn->ct; t0++)
  1367.         write(mn->fds[t0], buf, len);
  1368.     _exit(0);
  1369. }
  1370.  
  1371. void closeallelse(mn)        /**/
  1372. struct multio *mn;
  1373. {
  1374.     int t0, t1;
  1375.  
  1376.     for (t0 = 0; t0 != NOFILE; t0++)
  1377.     if (mn->pipe != t0) {
  1378.         for (t1 = 0; t1 != mn->ct; t1++)
  1379.         if (mn->fds[t1] == t0)
  1380.             break;
  1381.         if (t1 == mn->ct)
  1382.         close(t0);
  1383.     }
  1384. }
  1385.  
  1386. long int zstrtol(s, t, base)    /**/
  1387. char *s;
  1388. char **t;
  1389. int base;
  1390. {
  1391.     int ret = 0;
  1392.  
  1393.     if (base <= 10)
  1394.     for (; *s >= '0' && *s < ('0' + base); s++)
  1395.         ret = ret * base + *s - '0';
  1396.     else
  1397.     for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
  1398.          || (*s >= 'A' && *s < ('A' + base - 10)); s++)
  1399.         ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
  1400.     if (t)
  1401.     *t = (char *)s;
  1402.     return ret;
  1403. }
  1404.  
  1405. /* $(...) */
  1406.  
  1407. Lklist getoutput(cmd, qt)    /**/
  1408. char *cmd;
  1409. int qt;
  1410. {
  1411.     List list;
  1412.     int pipes[2];
  1413.     int pid;
  1414.  
  1415.     if (*cmd == '<') {
  1416.     int stream;
  1417.     char *fi, *s, x;
  1418.  
  1419.     for (cmd++; *cmd == ' '; cmd++);
  1420.     for (s = cmd; *s && *s != ' '; s++)
  1421.         if (*s == '\\')
  1422.         s++;
  1423.         else if (*s == '$')
  1424.         *s = String;
  1425.     x = *s;
  1426.     *s = '\0';
  1427.     fi = dupstring(cmd);
  1428.     *s = x;
  1429.     if (*fi == '~')
  1430.         *fi = Tilde;
  1431.     else if (*fi == '=')
  1432.         *fi = Equals;
  1433.     singsub(&fi);
  1434.     if (errflag)
  1435.         return NULL;
  1436.     stream = open(fi, O_RDONLY);
  1437.     if (stream == -1) {
  1438.         zerr("%e: %s", fi, errno);
  1439.         return NULL;
  1440.     }
  1441.     return readoutput(stream, qt);
  1442.     }
  1443.     if (!(list = parselstring(cmd)))
  1444.     return NULL;
  1445.     mpipe(pipes);
  1446.     blockchld();
  1447.     if ((cmdoutpid = pid = phork()) > 0) {
  1448.     Lklist retval;
  1449.  
  1450.     popheap();
  1451.     close(pipes[1]);
  1452.     retval = readoutput(pipes[0], qt);
  1453.     chldsuspend();        /* unblocks */
  1454.     lastval = cmdoutval;
  1455.     cmdoutval = 0;
  1456.     return retval;
  1457.     } else if (pid == -1) {
  1458.     popheap();
  1459.     close(pipes[0]);
  1460.     close(pipes[1]);
  1461.     errflag = 1;
  1462.     cmdoutpid = 0;
  1463.     unblockchld();
  1464.     return NULL;
  1465.     }
  1466.     unblockchld();
  1467.     subsh = 1;
  1468.     close(pipes[0]);
  1469.     redup(pipes[1], 1);
  1470.     entersubsh(0);
  1471.     sig_ignore(SIGTSTP);
  1472.     exiting = 1;
  1473.     execlist(list);
  1474.     close(1);
  1475.     _exit(lastval);
  1476.     zerr("exit returned in child!!", NULL, 0);
  1477.     kill(getpid(), SIGKILL);
  1478.     return NULL;        /* redundant but shuts up Convex cc */
  1479. }
  1480.  
  1481. /* read output of command substitution */
  1482.  
  1483. Lklist readoutput(in, qt)    /**/
  1484. int in;
  1485. int qt;
  1486. {
  1487.     Lklist ret;
  1488.     char *buf, *ptr;
  1489.     int bsiz, c, cnt = 0;
  1490.     FILE *fin;
  1491.  
  1492.     fin = fdopen(in, "r");
  1493.     ret = newlist();
  1494.     ptr = buf = (char *)ncalloc(bsiz = 64);
  1495.     if (qt) {
  1496.     *ptr++ = Nularg;
  1497.     cnt++;
  1498.     }
  1499.     while ((c = fgetc(fin)) != EOF)
  1500.     if (!qt && isep(c)) {
  1501.         if (cnt) {
  1502.         *ptr = '\0';
  1503.         addnode(ret, buf);
  1504.         ptr = buf = (char *)ncalloc(bsiz = 64);
  1505.         cnt = 0;
  1506.         }
  1507.     } else {
  1508.         *ptr++ = c;
  1509.         if (++cnt == bsiz) {
  1510.         char *pp = (char *)ncalloc(bsiz *= 2);
  1511.  
  1512.         memcpy(pp, buf, cnt);
  1513.         ptr = (buf = pp) + cnt;
  1514.         }
  1515.     }
  1516.     if (ptr != buf && ptr[-1] == '\n')
  1517.     ptr[-1] = '\0';
  1518.     else
  1519.     *ptr = '\0';
  1520.     if (cnt)
  1521.     addnode(ret, buf);
  1522.     fclose(fin);
  1523.     return ret;
  1524. }
  1525.  
  1526. /* =(...) */
  1527.  
  1528. char *getoutputfile(cmd)    /**/
  1529. char *cmd;
  1530. {
  1531.     int pid;
  1532.     char *nam = gettemp(), *str;
  1533.     List list;
  1534.  
  1535.     if (thisjob == -1)
  1536.     return NULL;
  1537.     for (str = cmd; *str && *str != Outpar; str++);
  1538.     if (!*str)
  1539.     zerr("oops.", NULL, 0);
  1540.     *str = '\0';
  1541.     if (!(list = parselstring(cmd)))
  1542.     return NULL;
  1543.     permalloc();
  1544.     if (!jobtab[thisjob].filelist)
  1545.     jobtab[thisjob].filelist = newlist();
  1546.     addnode(jobtab[thisjob].filelist, ztrdup(nam));
  1547.     heapalloc();
  1548.     blockchld();
  1549.     if ((pid = phork())) {
  1550.     popheap();
  1551.     if (pid < 0)
  1552.         unblockchld();
  1553.     else {
  1554.         waitforpid(pid);
  1555.         jobtab[thisjob].stat = 0;
  1556.     }
  1557.     return nam;
  1558.     }
  1559.     subsh = 1;
  1560.     close(1);
  1561.     entersubsh(0);
  1562.     (void)creat(nam, 0666);
  1563.     exiting = 1;
  1564.     execlist(list);
  1565.     close(1);
  1566.     _exit(lastval);
  1567.     zerr("exit returned in child!!", NULL, 0);
  1568.     kill(getpid(), SIGKILL);
  1569.     return NULL;        /* redundant but shuts up Convex cc */
  1570. }
  1571.  
  1572. /* get a temporary named pipe */
  1573.  
  1574. char *namedpipe()
  1575. {                /**/
  1576. #ifdef HAS_FIFOS
  1577.     char *tnam = gettemp();
  1578.  
  1579.     if (mknod(tnam, 0010666, 0) < 0)
  1580.     return NULL;
  1581.     return tnam;
  1582. #else
  1583.          return NULL;
  1584. #endif
  1585. }
  1586.  
  1587. /* <(...) */
  1588.  
  1589. char *getoutproc(cmd)        /**/
  1590. char *cmd;
  1591. {
  1592. #ifndef HAS_FIFOS
  1593.     zerr("doesn't look like your system supports FIFOs.", NULL, 0);
  1594.     return NULL;
  1595. #else
  1596.     List list;
  1597.     int fd;
  1598.     char *pnam, *str;
  1599.  
  1600.     if (thisjob == -1)
  1601.     return NULL;
  1602.     for (str = cmd; *str && *str != Outpar; str++);
  1603.     if (!*str)
  1604.     zerr("oops.", NULL, 0);
  1605.     *str = '\0';
  1606.     pnam = namedpipe();
  1607.     if (!pnam)
  1608.     return NULL;
  1609.     permalloc();
  1610.     if (!jobtab[thisjob].filelist)
  1611.     jobtab[thisjob].filelist = newlist();
  1612.     addnode(jobtab[thisjob].filelist, ztrdup(pnam));
  1613.     heapalloc();
  1614.     if (!(list = parselstring(cmd)))
  1615.     return NULL;
  1616.     if (phork()) {
  1617.     popheap();
  1618.     return pnam;
  1619.     }
  1620.     entersubsh(1);
  1621.     closem();
  1622.     fd = open(pnam, O_WRONLY);
  1623.     if (fd == -1) {
  1624.     zerr("can't open %s: %e", pnam, errno);
  1625.     _exit(1);
  1626.     }
  1627.     redup(fd, 1);
  1628.     fd = open("/dev/null", O_RDONLY);
  1629.     redup(fd, 0);
  1630.     exiting = 1;
  1631.     execlist(list);
  1632.     close(1);
  1633.     _exit(lastval);
  1634.     return NULL;
  1635. #endif
  1636. }
  1637.  
  1638. /* >(...) */
  1639.  
  1640. char *getinproc(cmd)        /**/
  1641. char *cmd;
  1642. {
  1643. #ifndef HAS_FIFOS
  1644.     zerr("doesn't look like your system supports FIFOs.", NULL, 0);
  1645.     return NULL;
  1646. #else
  1647.     List list;
  1648.     int pid, fd;
  1649.     char *pnam, *str;
  1650.  
  1651.     if (thisjob == -1)
  1652.     return NULL;
  1653.     for (str = cmd; *str && *str != Outpar; str++);
  1654.     if (!*str)
  1655.     zerr("oops.", NULL, 0);
  1656.     *str = '\0';
  1657.     pnam = namedpipe();
  1658.     if (!pnam)
  1659.     return NULL;
  1660.     permalloc();
  1661.     if (!jobtab[thisjob].filelist)
  1662.     jobtab[thisjob].filelist = newlist();
  1663.     addnode(jobtab[thisjob].filelist, ztrdup(pnam));
  1664.     heapalloc();
  1665.     if (!(list = parselstring(cmd)))
  1666.     return NULL;
  1667.     if ((pid = phork())) {
  1668.     popheap();
  1669.     return pnam;
  1670.     }
  1671.     entersubsh(1);
  1672.     closem();
  1673.     fd = open(pnam, O_RDONLY);
  1674.     redup(fd, 0);
  1675.     exiting = 1;
  1676.     execlist(list);
  1677.     _exit(lastval);
  1678.     return NULL;
  1679. #endif
  1680. }
  1681.  
  1682. /* > >(...) (does not use named pipes) */
  1683.  
  1684. int getinpipe(cmd)        /**/
  1685. char *cmd;
  1686. {
  1687.     List list;
  1688.     int pipes[2];
  1689.     char *str;
  1690.  
  1691.     for (str = cmd; *str && *str != Outpar; str++);
  1692.     if (!*str)
  1693.     zerr("oops.", NULL, 0);
  1694.     *str = '\0';
  1695.     if (!(list = parselstring(cmd + 2)))
  1696.     return -1;
  1697.     mpipe(pipes);
  1698.     if (phork()) {
  1699.     popheap();
  1700.     close(pipes[1]);
  1701.     return pipes[0];
  1702.     }
  1703.     close(pipes[0]);
  1704.     closem();
  1705.     entersubsh(1);
  1706.     redup(pipes[1], 1);
  1707.     exiting = 1;
  1708.     execlist(list);
  1709.     _exit(lastval);
  1710.     return 0;
  1711. }
  1712.  
  1713. /* < <(...) */
  1714.  
  1715. int getoutpipe(cmd)        /**/
  1716. char *cmd;
  1717. {
  1718.     List list;
  1719.     int pipes[2];
  1720.     char *str;
  1721.  
  1722.     for (str = cmd; *str && *str != Outpar; str++);
  1723.     if (!*str)
  1724.     zerr("oops.", NULL, 0);
  1725.     *str = '\0';
  1726.     if (!(list = parselstring(cmd + 2)))
  1727.     return -1;
  1728.     strinend();
  1729.     mpipe(pipes);
  1730.     if (phork()) {
  1731.     popheap();
  1732.     close(pipes[0]);
  1733.     return pipes[1];
  1734.     }
  1735.     close(pipes[1]);
  1736.     entersubsh(1);
  1737.     redup(pipes[0], 0);
  1738.     closem();
  1739.     exiting = 1;
  1740.     execlist(list);
  1741.     _exit(lastval);
  1742.     return 0;
  1743. }
  1744.  
  1745. /* run a list, saving the current job num */
  1746.  
  1747. void runlist(l)            /**/
  1748. List l;
  1749. {
  1750.     int cj = thisjob;
  1751.  
  1752.     execlist(l);
  1753.     thisjob = cj;
  1754. }
  1755.  
  1756. char *gettemp()
  1757. {                /**/
  1758.     return mktemp(dyncat((tmpprefix ? tmpprefix : DEFTMPPREFIX), "XXXXXX"));
  1759. }
  1760.  
  1761. /* my getwd */
  1762.  
  1763. char *zgetwd()
  1764. {                /**/
  1765.     static char buf0[MAXPATHLEN];
  1766.     char buf3[MAXPATHLEN];
  1767.  
  1768. #ifdef apollo
  1769.     char *buf2 = buf0 + 2;    /* changed +1 to +2  RBC 17.11.91 */
  1770.  
  1771. #else
  1772.     char *buf2 = buf0 + 1;
  1773.  
  1774. #endif
  1775.     struct stat sbuf;
  1776.     struct dirent *de;
  1777.     DIR *dir;
  1778.     ino_t ino, rootino = (ino_t) ~0;
  1779.     dev_t dev, rootdev = (dev_t) ~0;
  1780.  
  1781.     holdintr();
  1782.     buf2[0] = '\0';
  1783.     buf0[0] = '/';
  1784. #ifdef apollo
  1785.     buf0[1] = '/';        /* added RBC 17.11.91 */
  1786. #endif
  1787.     if (stat(buf0, &sbuf) >= 0) {
  1788.     rootino = sbuf.st_ino;
  1789.     rootdev = sbuf.st_dev;
  1790.     }
  1791.     for (;;) {
  1792.     if (stat(".", &sbuf) < 0) {
  1793.         chdir(buf0);
  1794.         noholdintr();
  1795.         return ztrdup(".");
  1796.     }
  1797.     ino = sbuf.st_ino;
  1798.     dev = sbuf.st_dev;
  1799.     if (stat("..", &sbuf) < 0) {
  1800.         chdir(buf0);
  1801.         noholdintr();
  1802.         return ztrdup(".");
  1803.     }
  1804.     if ((sbuf.st_ino == ino && sbuf.st_dev == dev) ||
  1805.         (ino == rootino && dev == rootdev)) {
  1806.         chdir(buf0);
  1807.         noholdintr();
  1808.         return ztrdup(buf0);
  1809.     }
  1810.     dir = opendir("..");
  1811.     if (!dir) {
  1812.         chdir(buf0);
  1813.         noholdintr();
  1814.         return ztrdup(".");
  1815.     }
  1816.     chdir("..");
  1817.     readdir(dir);
  1818.     readdir(dir);
  1819.     while ((de = readdir(dir)))
  1820.         if (de->d_ino == ino) {
  1821.         lstat(de->d_name, &sbuf);
  1822.         if (sbuf.st_dev == dev)
  1823.             goto match;
  1824.         }
  1825.     rewinddir(dir);
  1826.     readdir(dir);
  1827.     readdir(dir);
  1828.     while ((de = readdir(dir))) {
  1829.         lstat(de->d_name, &sbuf);
  1830.         if (sbuf.st_dev == dev)
  1831.         goto match;
  1832.     }
  1833.     noholdintr();
  1834.     closedir(dir);
  1835.     return ztrdup(".");
  1836.       match:
  1837.     strcpy(buf3, de->d_name);
  1838.     if (*buf2)
  1839.         strcat(buf3, "/");
  1840.     strcat(buf3, buf2);
  1841.     strcpy(buf2, buf3);
  1842.     closedir(dir);
  1843.     }
  1844. }
  1845.  
  1846. /* open pipes with fds >= 10 */
  1847.  
  1848. void mpipe(pp)            /**/
  1849. int *pp;
  1850. {
  1851.     pipe(pp);
  1852.     pp[0] = movefd(pp[0]);
  1853.     pp[1] = movefd(pp[1]);
  1854. }
  1855.  
  1856. /* do process substitution with redirection */
  1857.  
  1858. void spawnpipes(l)        /**/
  1859. Lklist l;
  1860. {
  1861.     Lknode n = firstnode(l);
  1862.     Redir f;
  1863.  
  1864.     for (; n; incnode(n)) {
  1865.     f = (Redir) getdata(n);
  1866.     if (f->type == OUTPIPE) {
  1867.         char *str = f->name;
  1868.  
  1869.         f->fd2 = getoutpipe(str);
  1870.     }
  1871.     if (f->type == INPIPE) {
  1872.         char *str = f->name;
  1873.  
  1874.         f->fd2 = getinpipe(str);
  1875.     }
  1876.     }
  1877. }
  1878.  
  1879. /* perform time ... command */
  1880.  
  1881. int exectime(cmd)        /**/
  1882. Cmd cmd;
  1883. {
  1884.     int jb = thisjob;
  1885.  
  1886.     if (!cmd->u.pline) {
  1887.     shelltime();
  1888.     return 0;
  1889.     }
  1890.     execpline(cmd->u.pline, TIMED, 0);
  1891.     thisjob = jb;
  1892.     return lastval;
  1893. }
  1894.  
  1895. /* define a function */
  1896.  
  1897. int execfuncdef(cmd)        /**/
  1898. Cmd cmd;
  1899. {
  1900.     Cmdnam cc;
  1901.     char *s;
  1902.  
  1903.     permalloc();
  1904.     while ((s = (char *)ugetnode(cmd->args))) {
  1905.     cc = (Cmdnam) zalloc(sizeof *cc);
  1906.     cc->flags = SHFUNC;
  1907.     if (!cmd->u.list)
  1908.         cc->u.list = NULL;
  1909.     else
  1910.         cc->u.list = (List) dupstruct(cmd->u.list);
  1911.     addhnode(ztrdup(s), cc, cmdnamtab, freecmdnam);
  1912.     if (!strncmp(s, "TRAP", 4)) {
  1913.         int t0 = getsignum(s + 4);
  1914.  
  1915.         if (t0 != -1) {
  1916.         settrap(t0, cmd->u.list);
  1917.         permalloc();
  1918.         }
  1919.     }
  1920.     }
  1921.     heapalloc();
  1922.     return 0;
  1923. }
  1924.  
  1925. /* evaluate a [[ ... ]] */
  1926.  
  1927. int execcond(cmd)        /**/
  1928. Cmd cmd;
  1929. {
  1930.     return !evalcond(cmd->u.cond);
  1931. }
  1932.  
  1933. void execshfunc(cmd, cn)    /**/
  1934. Cmd cmd;
  1935. Cmdnam cn;
  1936. {
  1937.     List l;
  1938.  
  1939.     if (errflag)
  1940.     return;
  1941.     if (!cn->u.list) {
  1942.     char *nam;
  1943.  
  1944.     if (!(cn->flags & PMFLAG_u))
  1945.         return;
  1946.     if (!(l = getfpfunc(nam = (char *)peekfirst(cmd->args)))) {
  1947.         zerr("function not found: %s", nam, 0);
  1948.         lastval = 1;
  1949.         return;
  1950.     }
  1951.     cn->flags &= ~PMFLAG_u;
  1952.     permalloc();
  1953.     cn->u.list = (List) dupstruct(l);
  1954.     heapalloc();
  1955.     popheap();
  1956.     }
  1957.     doshfunc(cn->u.list, cmd->args, cn->flags);
  1958. }
  1959.  
  1960. void doshfuncnoval(list, doshargs, flags)    /**/
  1961. List list;
  1962. Lklist doshargs;
  1963. int flags;
  1964. {
  1965.     int val = lastval;
  1966.  
  1967.     doshfunc(list, doshargs, flags);
  1968.     lastval = val;
  1969. }
  1970.  
  1971. void doshfunc(list, doshargs, flags)    /**/
  1972. List list;
  1973. Lklist doshargs;
  1974. int flags;
  1975. {
  1976.     char **tab, **x, *oargv0;
  1977.     int oxtr = opts[XTRACE], opev = opts[PRINTEXITVALUE], xexittr;
  1978.     int oldzoptind;
  1979.     Lklist olist;
  1980.     char *s;
  1981.     List xexitfn;
  1982.  
  1983.     pushheap();
  1984.     xexittr = sigtrapped[SIGEXIT];
  1985.     xexitfn = sigfuncs[SIGEXIT];
  1986.     tab = pparams;
  1987.     oargv0 = argzero;
  1988.     oldzoptind = zoptind;
  1989.     zoptind = 1;
  1990.     if (flags & PMFLAG_t)
  1991.     opts[XTRACE] = OPT_SET;
  1992.     opts[PRINTEXITVALUE] = OPT_UNSET;
  1993.     if (doshargs) {
  1994.     pparams = x = 
  1995.       (char **)zcalloc(((sizeof *x) * (1 + countnodes(doshargs))));
  1996.     argzero = ztrdup(ugetnode(doshargs));
  1997.     while ((*x = (char *)ugetnode(doshargs)))
  1998.         *x = ztrdup(*x), x++;
  1999.     } else {
  2000.     pparams = (char **)zcalloc(sizeof *pparams);
  2001.     argzero = ztrdup(argzero);
  2002.     }
  2003.     permalloc();
  2004.     olist = locallist;
  2005.     locallist = newlist();
  2006.     locallevel++;
  2007.     heapalloc();
  2008.     runlist(dupstruct(list));
  2009.     locallevel--;
  2010.     while ((s = (char *)getnode(locallist))) {
  2011.     Param pm = (Param) gethnode(s, paramtab);
  2012.  
  2013.     if (pm && pm->level > locallevel)
  2014.         unsetparam(s);
  2015.     zsfree(s);
  2016.     }
  2017.     zfree(locallist, sizeof(struct lklist));
  2018.     locallist = olist;
  2019.     breaks = retflag = 0;
  2020.     freearray(pparams);
  2021.     zsfree(argzero);
  2022.     zoptind = oldzoptind;
  2023.     argzero = oargv0;
  2024.     pparams = tab;
  2025.     if (sigfuncs[SIGEXIT] && sigfuncs[SIGEXIT] != xexitfn) {
  2026.     dotrap(SIGEXIT);
  2027.     freestruct(sigfuncs[SIGEXIT]);
  2028.     }
  2029.     sigtrapped[SIGEXIT] = xexittr;
  2030.     sigfuncs[SIGEXIT] = xexitfn;
  2031.     opts[XTRACE] = oxtr;
  2032.     opts[PRINTEXITVALUE] = opev;
  2033.     popheap();
  2034. }
  2035.  
  2036. /* search fpath for an undefined function */
  2037.  
  2038. List getfpfunc(s)        /**/
  2039. char *s;
  2040. {
  2041.     char **pp = fpath, buf[MAXPATHLEN];
  2042.     int fd;
  2043.  
  2044.     for (; *pp; pp++) {
  2045.     sprintf(buf, "%s/%s", *pp, s);
  2046.     if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY)) != -1) {
  2047.         int len = lseek(fd, 0, 2);
  2048.  
  2049.         if (len == -1)
  2050.         close(fd);
  2051.         else {
  2052.         char *d;
  2053.         List r;
  2054.  
  2055.         lseek(fd, 0, 0);
  2056.         d = (char *)zcalloc(len + 1);
  2057.         if (read(fd, d, len) != len) {
  2058.             zfree(d, len + 1);
  2059.             close(fd);
  2060.         } else {
  2061.             close(fd);
  2062.             r = parselstring(d);
  2063.             zfree(d, len + 1);
  2064.  
  2065.             return r;
  2066.         }
  2067.         }
  2068.     }
  2069.     }
  2070.     return NULL;
  2071. }
  2072.  
  2073. /* check to see if AUTOCD applies here */
  2074.  
  2075. extern int doprintdir;
  2076.  
  2077. char *cancd(s)            /**/
  2078. char *s;
  2079. {
  2080.     int nocdpath = s[0] == '.' &&
  2081.     (s[1] == '/' || !s[1] || (s[1] == '.' && (s[2] == '/' || !s[1])));
  2082.     char *t;
  2083.  
  2084.     if (*s != '/') {
  2085.     char sbuf[MAXPATHLEN], **cp;
  2086.  
  2087.     if (cancd2(s))
  2088.         return s;
  2089.     if (access(s, X_OK) == 0)
  2090.         return NULL;
  2091.     if (!nocdpath)
  2092.         for (cp = cdpath; *cp; cp++) {
  2093.         sprintf(sbuf, "%s/%s", *cp, s);
  2094.         if (cancd2(sbuf)) {
  2095.             doprintdir = -1;
  2096.             return dupstring(sbuf);
  2097.         }
  2098.         }
  2099.     if ((t = cd_able_vars(s))) {
  2100.         if (cancd2(t)) {
  2101.         doprintdir = -1;
  2102.         return t;
  2103.         }
  2104.     }
  2105.     return NULL;
  2106.     }
  2107.     return cancd2(s) ? s : NULL;
  2108. }
  2109.  
  2110. int cancd2(s)            /**/
  2111. char *s;
  2112. {
  2113.     struct stat buf;
  2114.  
  2115.     return !(access(s, X_OK) || stat(s, &buf) || !S_ISDIR(buf.st_mode));
  2116. }
  2117.